#ifndef MULTIGENEREGIONVISWIDGET_H
#define MULTIGENEREGIONVISWIDGET_H

#include <QObject>
#include <QPointer>
#include <QDebug>
#include <QThread>
#include <QGraphicsLinearLayout>
#include <QScrollArea>
#include <QGraphicsProxyWidget>
#include <QApplication>
#include <QDesktopWidget>
#include <QMutex>
#include <QWaitCondition>

#include "../GenoSageBaseClasses/genosagebasewidget.h"
#include "genomedatauploader.h"
#include "genomeprojectuploader.h"

#include "BaseDataManagement/genomedatabaseinterface.h"
#include "ProprietaryDataManagement/proprietarygenomelocaldatabaseinterface.h"
#include "PubMedDataManagement/pubmedgenomelocaldatabaseinterface.h"

#include "BaseDataManagement/genomeelementdatapacket.h"
#include "ProprietaryDataManagement/proprietarygenomeelementdatapacket.h"
#include "PubMedDataManagement/pubmedgenomeelementdatapacket.h"

#include "SageVis/VisLayouts/verticalstacklayout.h"

#include "SageVis/SageVisGuiElements/sagevistextlabel.h"
#include "SageVis/SageVisGuiElements/lineconnectorelement.h"

#include "contextviewerwidget.h"
#include "infobox.h"
#include "BaseDataManagement/datacontainer.h"

//NEED TO DO THIS FOR SIGNALS AND SLOTS WITH MY CUSTOM CLASSES TO WORK
#include <QMetaType>
Q_DECLARE_METATYPE(QList< QPointer < GenomeElementDataPacket> >)
Q_DECLARE_METATYPE(QList< QPointer < ContigDataPacket > >)
Q_DECLARE_METATYPE(QPointer< GenomeDatabaseInterface >)

//Forward declarations to avoid circular dependencies
class SageVis;
class GenoSage;
class Contig;
class GenomeElement;
class GeneRegionViewerMenuSideBar;

class VisBaseContainer;
class VisBaseHashContainer;

class VisBaseLayout;
class LayoutGenomeElements;



/**MultiGeneRegionVisWidget class.
  * This widget displays many 'gene regions'.
  * These gene regions may be from different genomes or different regions of the same genome.
  * By 'gene region' I mean a section of a genome where the primary 'entities' of interest are genes, promoters, operons, etc
  * rather than syntennic regions or other 'whole-genome-level' features.
  *
  * Now I am displaying all elements, but eventually will need to only display some- depending on user input, perhaps
  *
*/
class MultiGeneRegionVisWidget : public GenoSageBaseWidget
{
    Q_OBJECT
public:
    /** MultiGeneRegionVisWidget constructor.
      * Constructor requires genoSagePointer, sageVisPointer and QGraphicsItem parent.
      * Also takes a path to data, which may be null.
      * If no path to data is provided, constructor will handle getting data path or data files from user.
      * Once constructor has path to data, it will handle the upload and processing of this data
      * The parent will generally be sageVis, but in some cases this widget may be embedded on other widgets.
      */
    explicit MultiGeneRegionVisWidget( QPointer<GenoSage> genoSagePointer, QPointer<SageVis> sageVisPointer, QGraphicsItem *parent, QString* pathToData = 0);
        //Note:  I dislike using a pointer to a string here, but it is the only way to allow object to be created with no data file easily.
    ~MultiGeneRegionVisWidget();

//----------------------------------GETTING DATA AND DATA FILES--------------------------------------------------
    /** loadData() function.
      * This function launches file browsers and instructs the user to supply gff and fasta files
      * TO DO:  Allow user to specify multiple gff and fasta files
      * TO DO:  Handle user failing to specify a gff or fasta file
      */
    void getDataFromUser();

    /** getProjectDataFromUser() function.
      * TO DO: Empty right now, but eventually I think I will need a more thorough data upload interface
      * to capture info about file types from the user.
      * POTENTIAL CHANGE:  Also may need to move this to SAGENextPointer, so is on local computer.
      * CONCERN: I am concerned about locking out interactivity on other widgets while waiting for user input...
      */
    void getProjectDataFromUser();

    /** getGffFileFromUser() function.
      * This function launches a QFileDialog and asks to the user for a gff file.
      * TO DO: eventually I will need to modify this to take multiple gff files, or a set of gff files.
      */
    void getGffFileFromUser();

    /** getFastaFileFromUser() function.
      * This function launches a QFileDialog and asks to the user for a fasta file.
      * TO DO:  eventually I will need to modify this to take multiple fasta files, or a set of fasta files.
      */
    void getFastaFileFromUser();

    /** loadData() function.
      * This function launches a genomeDataUploader thread, which takes the gff and fasta files, processes them
      * to get genomeElementDataPackets, contigElementDataPackets and databaseInterface
      * Note- eventually, I may modify this to emit multiple genomeDataUploaders, to operate on files of different types.
      * When uploader is finished, dataUploadCompleteSlot is called.
      * This is Step 1 in data processing
      */
    void loadData();

    //GETTERS
    float getNtPerPixel();
    float getCenterXPosition();


//-----------------------------SHAPE AND SIZE FUNCTIONS---------------------------------
    void setGeometry(const QRectF &geom);
    void setGeometry(qreal x, qreal y, qreal w, qreal h);
    QRectF boundingRect() const;
    QPainterPath shape() const;

//---------------------------------PAINT FUNCTIONS--------------------------------------
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget);

    void saveProject(QString filename);
    void loadSavedProject(QString filename, QString dbName);

signals:
    
public slots:
    /** dataUploadCompleteSlot( QList< QPointer < GenomeElementDataPacket> >, QList< QPointer < ContigDataPacket > > , QPointer< GenomeDatabaseInterface >).
      * When uploader is finished, dataUploadCompleteSlot is called.
      * This slot recieves the newly created data, and 'new's the graphical objects to present this data
      * This is step 2 in data processing
      */
    void dataUploadCompleteSlot( QList< QPointer < GenomeElementDataPacket> >, QList< QPointer < ContigDataPacket > > , int);
    void createAndLayoutSlot(QList< QPointer < GenomeElementDataPacket> >, QList< QPointer < ContigDataPacket > > , int);

    void errorString(QString theError );

    void changeContigHeight(int newHeightFactor);
    void zoomChangeSlot(int newNtPerPixel);

    void sortByContigLength(bool longToShort);
    void sortByContigLengthLongToShort();
    void sortByContigLengthShortToLong();
    void sortByElementSlot(QString elt, QString field);
    void sortByElementSlot(QString elt);

    void giveColorToElementInContig(int contigIdx, QString elt, QString field);

    void showInfoBoxSlot( GenomeElementDataPacket*, GenomeElement* );
    void removeInfoBoxSlot( GenomeElementDataPacket*, GenomeElement* );

    void cacheThenCommit( QPointer< GenomeElementDataPacket > genomeElementDataPacket );
    void commitCache();

    void addEltToDBThenDelete( QPointer< GenomeElementDataPacket > genomeElementDataPacket );
    void addContigToDBThenDelete( QPointer< ContigDataPacket > contigDataPacket );

    void connectGenomeElements(QString key);//connect all elements with this key

    void receiveGenomeElementHoverSignal(bool hovering, QGraphicsSceneHoverEvent* event, QString key);
    void receiveColorChangeSignal(QColor c, QString key);
    //void receiveMoveToTopSignal(QString key);
    void receiveAlignmentSignal(float xPos, bool posStrand, QString parentContigId, QString key);
    void receiveCenteredSignal(QString key);

protected:
    /**
     *  mousePressEvent:  Click and drag to navigate initiated here
     */
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    /**
     *  mouseMoveEvent:  Click and drag to navigate carried out here
     */
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    /**
     *  mouseReleaseEvent:  Click and drag to navigate completed here
     */
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

private:
    //DATA FILES:
    QStringList gffFiles;
    QStringList fastaFiles;

    //DATA:
    QPointer<GenomeDatabaseInterface> databaseInterface;//not using yet
    //QList< QPointer < GenomeElementDataPacket > > genomeElementDataPackets;//data from gff file, plus output of cd hit
    //QList< QPointer < ContigDataPacket > > contigDataPackets;//data pertaining to the contig from gff file
    DataContainer* dataContainer;

    //VIS ITEMS
//    QList< QPointer < Contig > > contigs;//contigs contain sets of genome elements
//    QList< QPointer < GenomeElement > > genomeElements;//genome elements are genes, RNA's, promoters, etc.
//    QList< QPointer < SageVisTextLabel > > contigLabels;//labels adjacent to contigs

    //-----CACHE----
    QList< QPointer < GenomeElementDataPacket > > genomeElementDataPacketCache;
    int addedCount = 0;

    //ITEMS IN COLLECTIONS
    QPointer< VisBaseHashContainer > contigContainer;//container storing and managing contigs
    QPointer< VisBaseContainer > contigLabelContainer;//container storing and managing contig labels
    QPointer< VisBaseHashContainer > genomeElementContainer;//container storing and managing genome elements
        //genomeElementContainer Note:  genomeElementContainer is somewhat redundant, because genome elements are stored in separate contigContainers
        //          But, in the event that I need quick access to genome elements based on some property- eg. name- then I can
        //          take advantage of the hash container to quickly find all genomeElements with particular properties
    //QPointer< VisBaseContainer > lineConnectorContainer;//line connectors connect contigs to contig labels
        //Line Connector Note:  eventually it might make sense to abstract this away and allow programmer to specify any 2 containers
        //          and then designate a basis for line connecting elements.  Then the visbasewidget will take care of it
        //          automatically.

    //add list of genome labels
    QPointer< VisBaseHashContainer > genomeLabelContainer;


    //UPLOADER THREAD
    QThread* thread ;
    QMutex mutex;
    QWaitCondition doneAddingToDb;

    //OTHER WIDGETS
    ContextViewerWidget* contextViewerWidget;
    GeneRegionViewerMenuSideBar* menu;

    //CLICK AND DRAG TO NAVIGATE
    QPointF mousePressStartPt;
    bool clickAndDragInitialized;
    QRectF highlightRect;
    float deltaY;
    float deltaX;
    float prevX;

    //INFO BOXES
    InfoBox* infoBox;

    float centerXPos;

//    //VISUALIZATION PARAMETERS -- not using right now.  Eventually this will help with zoom and stuff
    float ntPerPixel;
    float totalNt;
    float ntPerPixelZoomFactor;

    int startContigIdx;
    int endContigIdx; //how many contigs are presently 'on-screen'

    //PRIVATE FUNCTIONS
    /** createGenomeElementsAndContigs( ).
      * When uploader is finished, dataUploadCompleteSlot, which then calls 'createGenomeElementsAndContigs()'.
      * This function creates the graphical objects- genomeElements and contigs.
      */
    void createGenomeElementsAndContigs();

    void createGenomeLabelsForContigsOnScreen();

    /** launchLayoutThread( ).
      * Once graphical objects are created, the layout is launched.
      * First the contigs and the contig labels are placed correctly in x
      * Then the contigs and contig label containers are given vertical stacked layouts to run.
      * Then the contigs are given genome element layouts to place the genome elements properly.
      */
    void launchLayoutThread();

    void determineContigsOnScreen();

    void clearAllContainersAndLists(); //called upon sorting- when need to repopulate scene

};

#endif // MULTIGENEREGIONVISWIDGET_H





/*
  Scratch:
    QGraphicsLinearLayout* contigLayout;
    QScrollArea* contigScrollArea;
    QGraphicsProxyWidget* contigScrollAreaProxy;


  */
